library Itemdex initializer init requires optional Table
    //**************************************************************
    //* Itemdex (Multi-version)
    //* -------
    //* I need this for many things, something that indexes items
    //* Problems of item indexing:
    //*  a) Only way to detect item death is with a dynamic trigger
    //*  b) No known way to detect an item removal event. Besides
    //*  expecting the user to tell you.
    //*  c) Runes leave some kind of ghost item with 0.0 life that
    //*  still got all the data of the item , including its type.
    //*
    //* ...So, I use a loop to do index garbage collection,
    //* see you in hell. Luckily it is even rarer to think of 8190
    //* items than to think of 8190 units so it should work ok...
    //*
    //* UserData or Table?
    //* ------------------
    //*   By setting the USE_USER_DATA constant to true, Itemdex will
    //* use ItemUserData instead of Table.
    //*
    //*   UserData is faster, if you don't use UserData for other things
    //* in your map, it is a good idea to use UserData.
    //*
    //*   If USE_USER_DATA is set to false, you will require Table in
    //* your map.
    //*
    //***************************************************************

    //===============================================================
    globals
        private constant boolean USE_USER_DATA = false
        private constant real RECYCLE_INTERVAL = 2.5
        private constant integer RECYCLE_COUNT = 10

    // Smaller RECYCLE_INTERVAL / Bigger RECYCLE_COUNT equal faster
    //  detection of removed items and worse performance.
    //
    // "Every RECYCLE_INTERVAL seconds RECYCLE_COUNT indexes are
    //  reviewed looking for removed/dead ones to recycle"
    endglobals

    //=============================================================
    private struct index
        item attachedto
    endstruct

    globals
        private index array activeIndexes
        private integer indexN = 0
        private Table cache
    endglobals

    function GetItemId takes item it returns integer
        local index d
        static if(USE_USER_DATA) then
            set d = index( GetItemUserData(it) )
        else
            set d = index( cache[GetHandleId(it)] )
        endif
        if(d == 0) then
            set d = index.create()
            set d.attachedto = it
            static if(USE_USER_DATA) then
                call SetItemUserData(it, integer(d) )
            else
                set cache[GetHandleId(it)] = integer(d)
            endif
            set activeIndexes[indexN] = d
            set indexN = indexN + 1
        endif
        return integer(d)
    endfunction

    // Requirements to use ItemdexModule:
    // : create takes no arguments
    // : no custom .destroy method
    //
    module Itemdex
        private static thistype array V
        private item attachedto = null

        method operator thatItem takes nothing returns item
            return this.attachedto
        endmethod

        private static method get takes nothing returns nothing
            //remind me to add .name to structs
        endmethod
        static method operator [] takes item it returns thistype
            local integer d = GetItemId(it)
            static if( DEBUG_MODE) then
                if ( (GetItemTypeId(it) == 0) or (GetWidgetLife(it) <= 0.0) ) then
                    call BJDebugMsg("Itemdex user error: ghost item used for " + thistype.get.name)
                endif
            endif

            if( (.V[d] != 0) and (.V[d].attachedto != it) ) then
                call .V[d].destroy()
                set  .V[d] = 0
            endif
            if ( .V[d] == 0) then
                set .V[d] = thistype.create()
                set .V[d].attachedto = it
            endif
            return .V[d]

        endmethod

        method destroy takes nothing returns nothing
            local integer d
            if ( this.attachedto != null) then
                set d = GetItemId( this.attachedto)
                set .V[d] = 0
                call deallocate()
                set this.attachedto = null
            else
                call deallocate()
            endif
        endmethod

        method release takes nothing returns nothing
            call destroy()
        endmethod
    endmodule

    globals
        private integer lastindex = 0
    endglobals

    private function recycleLoop takes nothing returns nothing
        local integer start
        local integer i
        local integer j

        if(indexN > 0) then
            //set start = lastindex % indexN
            set start = lastindex - indexN * (lastindex / indexN)
            set i = start
            set j = 0
            loop
                exitwhen (j >= RECYCLE_COUNT)
                if (GetItemTypeId(activeIndexes[i].attachedto) == 0) or (GetWidgetLife(activeIndexes[i].attachedto) == 0.0) then
                    set activeIndexes[i].attachedto = null
                    call activeIndexes[i].destroy()
                    set indexN = indexN - 1
                    set activeIndexes[i] = activeIndexes[ indexN ]
                endif

                set i = i + 1
                exitwhen (indexN == 0)
                if(i >= indexN) then
                    set i = 0
                endif
                if(start >= indexN) then
                    set start = 0
                endif
                exitwhen(i == start)
                set j = j + 1

            endloop
            set lastindex = i
        endif


    endfunction

    private function init takes nothing returns nothing
        local timer T = CreateTimer()
        static if( not USE_USER_DATA ) then
            set cache = Table.create()
        endif
        call TimerStart(T, RECYCLE_INTERVAL, true, function recycleLoop)
        set T = null
    endfunction

endlibrary
